package com.hua.api.modules.pay.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.github.binarywang.wxpay.bean.notify.SignatureHeader;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyV3Result;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyV3Result;
import com.github.binarywang.wxpay.bean.request.WxPayRefundV3Request;
import com.github.binarywang.wxpay.bean.result.WxPayRefundV3Result;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.hua.api.core.activemq.ActiveMqConstant;
import com.hua.api.core.activemq.MqMessageBuilder;
import com.hua.api.core.activemq.consumer.event.EventConstant;
import com.hua.api.core.constants.Status;
import com.hua.api.core.framework.util.exception.ApiError;
import com.hua.api.core.util.StringUtil;
import com.hua.api.core.util.ValidationUtil;
import com.hua.api.modules.home.service.ActiveMqService;
import com.hua.api.modules.order.dao.OrderInfoDao;
import com.hua.api.modules.order.domain.OrderInfo;
import com.hua.api.modules.pay.dao.OrderInfoPayinfoDao;
import com.hua.api.modules.pay.domain.OrderInfoPayinfo;
import com.hua.api.modules.pay.service.PayInfoService;
import com.hua.api.modules.pay.service.dto.OrderInfoPayinfoDto;
import com.hua.api.modules.pay.util.WechatPayUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Service
@RequiredArgsConstructor
public class PayInfoSericeImpl implements PayInfoService {


    private final OrderInfoPayinfoDao orderInfoPayinfoDao;

    private final OrderInfoDao orderInfoDao;

    private final WechatPayUtil wechatPayUtil;

    private final WxPayService wxPayService;

    private final ActiveMqService activeMqService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Map<String, String> notifyV3Order(String payNotifyForm, String timeStamp, String serial, String nonce, String signature) throws WxPayException {
        Map<String,String> resultMap = new HashMap<String,String>();
        resultMap.put("code","SUCCESS");
        resultMap.put("message","成功");
        SignatureHeader header = new SignatureHeader();
        header.setNonce(nonce);
        header.setTimeStamp(timeStamp);
        header.setSerial(serial);
        header.setSignature(signature);

        WxPayNotifyV3Result result = wxPayService.parseOrderNotifyV3Result(payNotifyForm,header);
        log.info("WxPayNotifyV3Result : {}",result.toString());
        log.info("result.getResult().toString() : {}",result.getResult().toString());
        WxPayNotifyV3Result.DecryptNotifyResult data = result.getResult();

        Status.PayTrade payTrade = Status.PayTrade.fromValue(data.getTradeState());
        if(payTrade== Status.PayTrade.success){
            log.warn("支付回调成功 SUCCESS:  {}",data);
            // 支付成功
            updateOnPaySuccess(data);
        }else {
            log.warn("支付回调通知异常 FAIL:{}",data);
        }

        return resultMap;
    }

    @Transactional(rollbackFor = Exception.class)
    public void updateOnPaySuccess(WxPayNotifyV3Result.DecryptNotifyResult data){
        OrderInfoPayinfo payinfo = orderInfoPayinfoDao.findOrderInfoPayinfo(data.getOutTradeNo());
        if(payinfo==null){
            log.warn("notifyV3Order warn : outTradeNo={}, result= {}",data.getOutTradeNo(),data);
        }
        Status.PayTrade  currentPayTrade = Status.PayTrade.fromValue(payinfo.getState());
        if(currentPayTrade == Status.PayTrade.nopay){
            log.info("updateOnPaySuccess = {}",data);
            // 未支付状态才操作， 避免多次提醒后多次操作
            payinfo.setResult(data.toString());
            payinfo.setUpdateTime(new Date());
            payinfo.setThirdTradeNo(data.getTransactionId());
            payinfo.setState(data.getTradeState());
            orderInfoDao.updateObject(payinfo);

            OrderInfo orderInfo = orderInfoDao.findOrderInfoWithOrderNo(payinfo.getOrderNo());
            if(orderInfo!=null){
                Status.OrderStatus orderStatus = Status.OrderStatus.fromValue(orderInfo.getStatus());
                if(orderStatus== Status.OrderStatus.tijiao){
                    // 更新订单的支付状态。
                    orderInfo.setStatus(Status.OrderStatus.yijiedan.getValue());
                    orderInfoDao.updateObject(orderInfo);
                    //
                    JSONObject message =  MqMessageBuilder.builder()
                            .event(EventConstant.event_order_created)
                            .target(ActiveMqConstant.hua000_api_queue)
                            .set("order_no",orderInfo.getOrderNo())
                            .set("timestamp",System.currentTimeMillis())
                            .build();
                    //
                    activeMqService.sendQueue(ActiveMqConstant.hua000_api_queue,message.toJSONString());

                }else{
                    log.warn("订单支付状态异常， 非待支付状态.当前状态={}",orderInfo.getStatus());
                }
            }

        }else {
            log.warn("支付多次提醒 outTradeNo={},currentPayTrade={}",data.getOutTradeNo(),currentPayTrade.getName());
        }

    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public JSONObject payinfoRefundUseOutTradeNo(String outTradeNo, OrderInfoPayinfoDto dto) {
        log.info("PayInfoSericeImpl.payinfoRefundUseOutTradeNo(outTradeNo={}) ",outTradeNo);
        JSONObject data = new JSONObject();
        OrderInfoPayinfo payinfo = orderInfoPayinfoDao.findOrderInfoPayinfoUseOrderOutTradeNo(outTradeNo);
        ValidationUtil.requireNotNull(payinfo,"支付记录不存在，退款失败！");
        Status.PayTrade payTrade = Status.PayTrade.fromValue(payinfo.getState());
        if(payTrade!=Status.PayTrade.success){
            throw ApiError.Type.LOGIC_ERROR.toException("支付记录状态异常 payTrade="+payinfo.getState());
        }

        Status.PayRefund payRefund = Status.PayRefund.fromValue(payinfo.getRefundStatus());
        if(payRefund!=Status.PayRefund.none){
            throw ApiError.Type.LOGIC_ERROR.toException("支付记录退款状态异常 payRefund="+payinfo.getRefundStatus());
        }

        OrderInfo orderInfo = orderInfoDao.findOrderInfoWithOrderNo(payinfo.getOrderNo());
        ValidationUtil.requireNotNull(orderInfo,"退款订单记录不存在，退款失败！");
        Status.OrderStatus orderStatus = Status.OrderStatus.fromValue(orderInfo.getStatus());
        if(orderStatus!= Status.OrderStatus.quxiao){
            throw ApiError.Type.LOGIC_ERROR.toException("订单状态异常，只有取消的订单才能退款 status="+orderStatus.getValue()+" orderNo="+orderInfo.getOrderNo());
        }
        ValidationUtil.requireNotNull(dto.getRefundMoney(),"请输入退款金额！");

        if(dto.getRefundMoney()>payinfo.getPayMoney()){
            throw ApiError.Type.LOGIC_ERROR.toException("退款金额大于用户支付金额 getRefundMoney="+dto.getRefundMoney()+" payMoney="+payinfo.getPayMoney());
        }

        // 微信支付
        if(StringUtil.equals(payinfo.getPayType(),Status.SOURCE_WECHAT)){
            payinfo.setOutRefundNo(wechatPayUtil.createWechatOutRefundNo(19));
            payinfo.setRefundReson(dto.getRefundReson());
            payinfo.setRefundStatus(Status.PayRefund.processing.getValue());
            payinfo.setRefundMoney(dto.getRefundMoney());
            orderInfoDao.updateObject(payinfo);
            WxPayRefundV3Request request = wechatPayUtil.getWxPayRefundV3Request(payinfo);

            try {
                WxPayRefundV3Result result = wxPayService.refundV3(request);
                log.info("WxPayRefundV3Request result.getStatus()={}",result.getStatus());
                log.info("WxPayRefundV3Request result.getRefundId()={}",result.getRefundId());
                log.info("WxPayRefundV3Request result.getTransactionId()={}",result.getTransactionId());
                data.put("data",result);
                // result.getStatus();
            } catch (WxPayException e) {
                throw ApiError.Type.LOGIC_ERROR.toException("退款异常 msg="+e.getMessage());
            }

        }

        return data;
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public Map<String, String> notifyV3Refund(String payNotifyForm, String timeStamp, String serial, String nonce, String signature) throws WxPayException {
        Map<String,String> resultMap = new HashMap<>();
        resultMap.put("code","SUCCESS");
        resultMap.put("message","成功");
        SignatureHeader header = new SignatureHeader();
        header.setNonce(nonce);
        header.setTimeStamp(timeStamp);
        header.setSerial(serial);
        header.setSignature(signature);

        WxPayRefundNotifyV3Result result = wxPayService.parseRefundNotifyV3Result(payNotifyForm,header);
        log.info("WxPayNotifyV3Result : {}",result.toString());
        log.info("result.getResult().toString() : {}",result.getResult().toString());
        WxPayRefundNotifyV3Result.DecryptNotifyResult data = result.getResult();
        updateonRefundSuccess(data);
        return resultMap;
    }


    // 退款成功
    @Transactional(rollbackFor = Exception.class)
    public void updateonRefundSuccess(WxPayRefundNotifyV3Result.DecryptNotifyResult data){
        OrderInfoPayinfo payinfo = orderInfoPayinfoDao.fineOrderInfoPayinfoUseRefundNo(data.getOutRefundNo());
        if(payinfo==null){
            log.warn("updateonRefundSuccess warn : getOutRefundNo={}, result= {}",data.getOutRefundNo(),data);
        }else{
            Status.PayRefund  payRefundStatus = Status.PayRefund.fromValue(data.getRefundStatus());
            if(payRefundStatus == Status.PayRefund.success){
                // 退款成功
                Status.PayRefund  currentPayRefund = Status.PayRefund.fromValue(payinfo.getRefundStatus());
                if(currentPayRefund == Status.PayRefund.processing){
                    //
                    log.info("退款成功，执行退款业务 getOutRefundNo={}",data.getOutRefundNo());
                    payinfo.setRefundResult(data.toString());
                    payinfo.setRefundTime(new Date());
                    payinfo.setRefundStatus(data.getRefundStatus());
                    orderInfoDao.updateObject(payinfo);
                }else {
                    log.warn("退款状态异常，getOutRefundNo={}",data.getOutRefundNo());
                }

            }else {
                log.warn("退款状态失败，getOutRefundNo={}",data.getOutRefundNo());
            }
        }
    }

}
